﻿module InstructionParser
open SVM
open SVMLibrary
open InstructionManager

let parse_line (line : char list) : string list =
  
  //this function removes white spaces or tabs from a program line   
  let rec clear_white_spaces (line : char list) (acc1 : char list) (acc2 : char list list) : char list list =
    match line with
    | [] -> 
      if acc1 <> [] then
        acc2 @ [acc1]
      else
        acc2
    | c :: cs when c = ' ' || c = '\t' ->
        if acc1 <> [] then
          clear_white_spaces cs [] (acc2 @ [acc1])
        else
          clear_white_spaces cs [] acc2
    | c :: cs ->
        clear_white_spaces cs (acc1 @ [c]) acc2
    
  (clear_white_spaces line [] []) |> List.map(fun x -> list_to_string x)

//return an updated svm and a boolean telling if END has been reached
let execute_line  (line : string)
                  (svm : float list * float list * int * bool * (int * int) list) : float list * float list * int * bool * (int * int) list =
  let parsed_line = parse_line (string_to_list line)
  let (m,r,pc,e,labels) = svm
  match parsed_line with
  | ["NOP"] -> (m,r,pc + 1,e,labels)
  | ["MOV";arg1;arg2] -> mov arg1 arg2 svm
  | ["AND";arg1;arg2] -> _and arg1 arg2 svm
  | ["OR";arg1;arg2] -> _or arg1 arg2 svm
  | ["NOT";arg1;arg2] -> _not arg1 arg2 svm
  | ["ADD";arg1;arg2] -> arith_op arg1 arg2 svm (+)
  | ["SUB";arg1;arg2] -> arith_op arg1 arg2 svm (-)
  | ["MUL";arg1;arg2] -> arith_op arg1 arg2 svm (*)
  | ["DIV";arg1;arg2] -> arith_op arg1 arg2 svm (/)
  | ["MOD";arg1;arg2] -> arith_op arg1 arg2 svm (%)
  | ["EQ";arg1;arg2] -> comp_op arg1 arg2 svm (=)
  | ["GRT";arg1;arg2] -> comp_op arg1 arg2 svm (>)
  | ["LST";arg1;arg2] -> comp_op arg1 arg2 svm (<)
  | ["GEQ";arg1;arg2] -> comp_op arg1 arg2 svm (>=)
  | ["LEQ";arg1;arg2] -> comp_op arg1 arg2 svm (<=)
  | ["JMP";arg] -> jump arg svm
  | ["JC";arg1;arg2] -> conditional_jump arg1 arg2 svm
  | ["LBL";arg] -> (m,r,pc + 1,e,labels)
  | ["END"] -> _end svm
  | _ -> failwith ("Unsupported instruction" + parsed_line.Head)

let preprocess_label (line : string)
                     (svm : float list * float list * int * bool * (int * int) list) : float list * float list * int * bool * (int * int) list =

  let parsed_line = parse_line (string_to_list line)
  let (m,r,pc,e,labels) = svm
  match parsed_line with
  | ["LBL";arg] -> label arg svm
  | _ -> (m,r,pc + 1,e,labels)

let rec preprocess_labels (program : string list)
                          (svm : float list * float list * int * bool * (int * int) list) : float list * float list * int * bool * (int * int) list =
                      
  match program with
  | [] -> svm
  | line :: program ->
      let updated_svm = preprocess_label line svm
      preprocess_labels program updated_svm

    

let rec run_program (program : string list)
                    (svm : float list * float list * int * bool * (int * int) list) : string =
  let (_,_,pc,e,_) = svm
  if e then 
    dump svm
  else
    if pc < (program |> List.length) then
      if e then
        dump svm
      else
        let updated_svm = execute_line (List.nth program pc) svm
        run_program program updated_svm
    else
      failwith "Reached the end of the program but no END instruction found"
  
